/**
 * 数据转换工具
 * 支持不同数据格式与内部要素的转换
 */

import { Feature } from 'ol';
import { Point, LineString, Polygon, Geometry } from 'ol/geom';
import { fromLonLat, toLonLat } from 'ol/proj';
import { Coordinate, FeatureData, TrackPoint } from '../types';

/**
 * 数据转换工具类
 */
export class DataUtil {
  /**
   * 将坐标转换为OpenLayers坐标（EPSG:3857）
   * @param coordinate 经纬度坐标 [经度, 纬度]
   * @returns OpenLayers坐标
   */
  static toOlCoordinate(coordinate: Coordinate): [number, number] {
    // Coordinate 类型就是 [number, number]，但 TypeScript 需要明确断言
    // @ts-expect-error - Coordinate 和 [number, number] 在运行时是兼容的
    return fromLonLat(coordinate);
  }

  /**
   * 将OpenLayers坐标转换为经纬度
   * @param coordinate OpenLayers坐标
   * @returns 经纬度坐标 [经度, 纬度]
   */
  static fromOlCoordinate(coordinate: [number, number]): Coordinate {
    return toLonLat(coordinate) as Coordinate;
  }

  /**
   * 创建点要素
   * @param coordinate 坐标
   * @param properties 属性
   * @returns Feature对象
   */
  static createPointFeature(
    coordinate: Coordinate,
    properties?: Record<string, any>
  ): Feature<Point> {
    const olCoord = this.toOlCoordinate(coordinate);
    const geometry = new Point(olCoord);
    const feature = new Feature(geometry);
    
    if (properties) {
      feature.setProperties(properties);
    }
    
    return feature;
  }

  /**
   * 创建线要素
   * @param coordinates 坐标数组
   * @param properties 属性
   * @returns Feature对象
   */
  static createLineFeature(
    coordinates: Coordinate[],
    properties?: Record<string, any>
  ): Feature<LineString> {
    const olCoords = coordinates.map(coord => this.toOlCoordinate(coord));
    const geometry = new LineString(olCoords);
    const feature = new Feature(geometry);
    
    if (properties) {
      feature.setProperties(properties);
    }
    
    return feature;
  }

  /**
   * 创建面要素
   * @param coordinates 坐标数组（外环）
   * @param holes 内环坐标数组（可选）
   * @param properties 属性
   * @returns Feature对象
   */
  static createPolygonFeature(
    coordinates: Coordinate[],
    holes?: Coordinate[][],
    properties?: Record<string, any>
  ): Feature<Polygon> {
    const olCoords = coordinates.map(coord => this.toOlCoordinate(coord));
    const holesOl = holes?.map(hole => hole.map(coord => this.toOlCoordinate(coord)));
    const geometry = new Polygon([olCoords, ...(holesOl || [])]);
    const feature = new Feature(geometry);
    
    if (properties) {
      feature.setProperties(properties);
    }
    
    return feature;
  }

  /**
   * 从FeatureData创建要素
   * @param data FeatureData对象
   * @returns Feature对象
   */
  static createFeatureFromData(data: FeatureData): Feature<Geometry> {
    const { geometry, properties } = data;
    
    // 判断几何类型
    if (Array.isArray(geometry)) {
      if (geometry.length > 0 && Array.isArray(geometry[0])) {
        // 可能是线或面
        if (geometry[0].length > 0 && Array.isArray(geometry[0][0])) {
          // 面 - geometry 是 Coordinate[][]
          const rings = geometry as unknown as Coordinate[][];
          return this.createPolygonFeature(rings[0], rings.slice(1), properties);
        } else {
          // 线 - geometry 是 Coordinate[]
          return this.createLineFeature(geometry as Coordinate[], properties);
        }
      } else {
        // 点 - geometry 是 Coordinate
        return this.createPointFeature(geometry as Coordinate, properties);
      }
    } else {
      // 点
      return this.createPointFeature(geometry as Coordinate, properties);
    }
  }

  /**
   * 从要素获取FeatureData
   * @param feature Feature对象
   * @returns FeatureData对象
   */
  static getDataFromFeature(feature: Feature<Geometry>): FeatureData {
    const geometry = feature.getGeometry();
    if (!geometry) {
      throw new Error('Feature has no geometry');
    }

    let coords: Coordinate | Coordinate[];
    
    if (geometry instanceof Point) {
      coords = this.fromOlCoordinate(geometry.getCoordinates() as [number, number]);
    } else if (geometry instanceof LineString) {
      const olCoords = geometry.getCoordinates();
      // @ts-expect-error - Coordinate 和 [number, number] 在运行时是兼容的
      coords = olCoords.map((coord: [number, number]) => 
        this.fromOlCoordinate(coord as Coordinate)
      );
    } else if (geometry instanceof Polygon) {
      const coordinates = geometry.getCoordinates();
      // @ts-expect-error - Coordinate 和 [number, number] 在运行时是兼容的
      coords = coordinates[0].map((coord: [number, number]) => 
        this.fromOlCoordinate(coord as Coordinate)
      );
    } else {
      throw new Error('Unsupported geometry type');
    }

    return {
      id: feature.getId(),
      geometry: coords,
      properties: feature.getProperties()
    };
  }

  /**
   * 从GeoJSON格式创建要素数组
   * @param geoJson GeoJSON对象
   * @returns Feature数组
   */
  static fromGeoJSON(geoJson: any): Feature<Geometry>[] {
    const features: Feature<Geometry>[] = [];
    
    if (geoJson.type === 'FeatureCollection') {
      geoJson.features.forEach((feature: any) => {
        features.push(this.fromGeoJSONFeature(feature));
      });
    } else if (geoJson.type === 'Feature') {
      features.push(this.fromGeoJSONFeature(geoJson));
    }
    
    return features;
  }

  /**
   * 从GeoJSON Feature创建要素
   * @param geoJsonFeature GeoJSON Feature对象
   * @returns Feature对象
   */
  private static fromGeoJSONFeature(geoJsonFeature: any): Feature<Geometry> {
    const { geometry, properties, id } = geoJsonFeature;
    const olGeometry = this.geoJSONGeometryToOl(geometry);
    const feature = new Feature(olGeometry);
    
    if (properties) {
      feature.setProperties(properties);
    }
    
    if (id !== undefined) {
      feature.setId(id);
    }
    
    return feature;
  }

  /**
   * 将GeoJSON几何转换为OpenLayers几何
   * @param geoJsonGeometry GeoJSON几何对象
   * @returns OpenLayers几何对象
   */
  private static geoJSONGeometryToOl(geoJsonGeometry: any): Geometry {
    const { type, coordinates } = geoJsonGeometry;
    
    switch (type) {
      case 'Point':
        return new Point(fromLonLat(coordinates));
      case 'LineString':
        return new LineString(coordinates.map((coord: [number, number]) => fromLonLat(coord)));
      case 'Polygon':
        return new Polygon(
          coordinates.map((ring: [number, number][]) => 
            ring.map((coord: [number, number]) => fromLonLat(coord))
          )
        );
      case 'MultiPoint':
        return new Point(fromLonLat(coordinates[0]));
      case 'MultiLineString':
        return new LineString(
          coordinates[0].map((coord: number[]) => fromLonLat(coord))
        );
      case 'MultiPolygon':
        return new Polygon(
          coordinates[0][0].map((coord: number[]) => fromLonLat(coord))
        );
      default:
        throw new Error(`Unsupported geometry type: ${type}`);
    }
  }

  /**
   * 将要素数组转换为GeoJSON格式
   * @param features Feature数组
   * @returns GeoJSON对象
   */
  static toGeoJSON(features: Feature<Geometry>[]): any {
    return {
      type: 'FeatureCollection',
      features: features.map(feature => {
        const geometry = feature.getGeometry();
        if (!geometry) {
          return null;
        }

        const geoJsonGeometry = this.olGeometryToGeoJSON(geometry);
        return {
          type: 'Feature',
          geometry: geoJsonGeometry,
          properties: feature.getProperties(),
          id: feature.getId()
        };
      }).filter(Boolean)
    };
  }

  /**
   * 将OpenLayers几何转换为GeoJSON几何
   * @param geometry OpenLayers几何对象
   * @returns GeoJSON几何对象
   */
  private static olGeometryToGeoJSON(geometry: Geometry): any {
    if (geometry instanceof Point) {
      const coord = toLonLat(geometry.getCoordinates() as [number, number]);
      return {
        type: 'Point',
        coordinates: coord
      };
    } else if (geometry instanceof LineString) {
      const olCoords = geometry.getCoordinates();
      // @ts-expect-error - Coordinate 和 [number, number] 在运行时是兼容的
      const coords = olCoords.map((coord: [number, number]) => 
        toLonLat(coord) as Coordinate
      );
      return {
        type: 'LineString',
        coordinates: coords
      };
    } else if (geometry instanceof Polygon) {
      const olCoords = geometry.getCoordinates();
      // @ts-expect-error - Coordinate 和 [number, number] 在运行时是兼容的
      const coords = olCoords.map((ring: [number, number][]) =>
        ring.map((coord: [number, number]) => 
          toLonLat(coord) as Coordinate
        )
      ) as Coordinate[][];
      return {
        type: 'Polygon',
        coordinates: coords
      };
    } else {
      throw new Error('Unsupported geometry type');
    }
  }

  /**
   * 从轨迹点数组创建线要素
   * @param trackPoints 轨迹点数组
   * @returns Feature对象
   */
  static createTrackLine(trackPoints: TrackPoint[]): Feature<LineString> {
    const coordinates = trackPoints.map(point => this.toOlCoordinate(point.coordinate));
    const geometry = new LineString(coordinates);
    const feature = new Feature(geometry);
    
    // 存储轨迹点数据
    feature.set('trackPoints', trackPoints);
    
    return feature;
  }
}

